from typing import Optional, List, Annotated, TypeVar, Generic, Any

from pydantic import BaseModel, ConfigDict, create_model, Field, PrivateAttr, ValidationError
from sqlalchemy import String, ARRAY
from sqlalchemy.orm import mapped_column, Mapped, DeclarativeBase


class User(BaseModel):
    id: int
    name: str = 'Jane Doe'


# 发生了强制类型转换
user = User(id='123', name='John Doe')
assert user.id == 123
assert isinstance(user.id, int)
#
# 记录显式设置的字段，是一个dict
print('model_fields_set 属性 ：', user.model_fields_set, '\n')
assert 'id' in user.model_fields_set
#
# 把象转换成字典的方法 ： dict()（已弃用） ， model_dump()
# model_dump() 会递归地将嵌套模型转换为字典，而dict() 只会返回最外层的字典。
# print('dict()方法 :', user.dict(), ' ; ', ) # 注意 ： 在pydantic 2.0 中，dict() 已被弃用，
print('model_dump()方法 ：', user.model_dump(), '\n')
print('model_dump_json()方法 ：', user.model_dump_json(), '\n')
# assert user.model_dump() == {'id': 123, 'name': 'Jane Doe'}
#
# 强制类型转换
print('--------------------------- 强制类型转换 --------------------------------------')
# Pydantic 可以强制转换输入数据以强制其符合模型字段类型
user = User(id='123', name='John Doe')
print('强制类型转换 ：', user.model_validate({'id': '123', 'name': 'John Doe'}))
#
# # 额外数据
# print('--------------------------- 额外数据 --------------------------------------')
# 默认情况下，当你提供额外的数据时，Pydantic 模型不会出错，这些值将被忽略
# User 类 并没有 x，y 属性
user = User(id='123', name='John Doe', x=12, y=14)
print('额外数据 ：', user)
#
#
class XModel(BaseModel):
    x: int


m = XModel(x=1, y='a')
# assert m.model_dump() == {'x': 1, 'y': 'a'}
# assert m.__pydantic_extra__ == {'y': 'a'}
#
# # 模型的属性
# print('--------------------------- 模型属性 --------------------------------------')
# # model_fields：字段名称与其定义之间的映射（FieldInfo 实例）。
# print('model_fields 属性 ：', user.model_fields, '\n')
# # model_computed_fields：计算域名称与其定义（ComputedFieldInfo 实例）之间的映射。
#
# # model_extra：验证期间设置的额外字段。
#
# # model_fields_set：初始化模型时显式提供的字段集。
# print('model_fields_set ：', user.model_fields_set, '\n')
#
# # 模型方法
# print('--------------------------- 模型方法 --------------------------------------')
# # model_validate（）：根据 Pydantic 模型验证给定对象。请参阅验证数据。
#
# # model_validate_json（） ：根据 Pydantic 模型验证给定的 JSON 数据。请参阅验证数据。
#
# # model_construct（）：创建模型而不运行验证。请参阅创建未经验证的模型。
# print('model_construct()方法 ：', User.model_construct(), '返回值数据类型 ：', type(User.model_construct()), '\n')
#
# # model_dump（）：返回模型的字段和值的字典。请参见序列化。
# print('model_dump()方法 ：', user.model_dump(), '返回值的数据类型 ：', type(user.model_dump()), '\n')
#
# # model_dump_json（）：返回 model_dump（） 的 JSON 字符串表示形式。请参见序列化。
# # 返回模型的一些基本信息，即元信息
# print('model_dump_json()方法 ：', user.model_dump_json(), '返回值的数据类型 ：', type(user.model_dump_json()), '\n')
#
# # model_copy（） ：返回模型的副本（默认情况下为浅表副本）。请参见序列化。
# print('model_copy()方法 ：', user.model_copy(), '\n')
# # model_json_schema（） ：返回表示模型的 JSON 架构的 jsonable 字典。请参阅 JSON 架构。
# print('model_json_schema()方法 ：', user.model_json_schema(), '返回值的数据类型 ：', type(user.model_json_schema()), '\n')
#
# # model_parametrized_name（） ：计算泛型类参数化的类名。
#
# # model_post_init（）：在实例化模型并应用所有字段验证器后执行其他作。
#
# # model_rebuild（）：重新构建模型架构，该架构还支持构建递归泛型模型。 请参阅重新构建模型架构。
#
#
# # 嵌套模型
# print('--------------------------- 嵌套模型 --------------------------------------')
#
# #
# class Foo(BaseModel):
#     count: int
#     size: Optional[float] = None
#
#
# class Bar(BaseModel):
#     apple: str = 'x'
#     banana: str = 'y'
#
#
# class Spam(BaseModel):
#     foo: Foo
#     bars: List[Bar]
#
#
# m = Spam(foo={'count': 4}, bars=[{'apple': 'x1'}, {'apple': 'x2'}])
#
# print('对象 转 字典 ：', m.model_dump(), ';\n对象 转 json字符串 ：', m.model_dump_json())
#
# # 重新构建模型架构
# print('--------------------------- 重新构建模型架构 --------------------------------------')
#
#
# # 一般 B模型 引用 A模型 作为属性，那么 A模型 必须先于 B模型 定义，这样 B模型 才能找到 A模型；
# # B模型 调用 model_rebuild() 方法后，会在模块中全局查找 A模型 ，从而完成 B模型的构建；
#
# class XXFoo(BaseModel):
#     # x: 'Bar' # 这样写 ，它会自动找到前面定义的 Bar 模型
#     x: 'Bar2'  # 这样写 ，会报错，因为在前面并没有定义 Bar2 模型
#
#
# try:
#     XXFoo.model_json_schema()
#     print('Foo.model_json_schema()方法 ：', XXFoo.model_json_schema())
# except PydanticUserError as e:
#     print('错误信息 ： ', e)
# """
# 错误信息 ： `XXFoo` is not fully defined; you should define `Bar2`, then call `XXFoo.model_rebuild()`.
# """
#
# #  model_rebuild() 会在此模块中全局搜索  Bar2 模型，找到就用
# Foo.model_rebuild()
# print('方法 ： ', Foo.model_json_schema())
#
#
# class Bar2(BaseModel):
#     name: str
#
#
# # 任意类实例
# print('--------------------------- 任意类实例 --------------------------------------')
#
#
# # Pydantic 模型也可以通过读取相应的实例属性从任意类实例创建 添加到模型字段名称中。此功能的一个常见应用是与对象关系映射集成 （ORM） 的 ORM 中。
# # 为此，请将 from_attributes config 值设置为 （有关更多详细信息，请参阅 Configuration 上的文档）。
# # 【理解】：2个模型，只要他们有相同的字段名称，那么就可以相互转换
# class Base(DeclarativeBase):
#     pass
#
#
# class CompanyOrm(Base):
#     __tablename__ = 'companies'
#
#     id: Mapped[int] = mapped_column(primary_key=True, nullable=False)
#     public_key: Mapped[str] = mapped_column(
#         String(20), index=True, nullable=False, unique=True
#     )
#     domains: Mapped[list[str]] = mapped_column(ARRAY(String(255)))
#
#
# class CompanyModel(BaseModel):
#     model_config = ConfigDict(from_attributes=True)
#
#     id: int
#     public_key: Annotated[str, StringConstraints(max_length=20)]
#     domains: list[Annotated[str, StringConstraints(max_length=255)]]
#
#
# co_orm = CompanyOrm(
#     id=123,
#     public_key='foobar',
#     domains=['example.com', 'foobar.com'],
# )
# print(co_orm)
# # > <__main__.CompanyOrm object at 0x0123456789ab>
# co_model = CompanyModel.model_validate(co_orm)
# print(co_model)
#
#
# # > id=123 public_key='foobar' domains=['example.com', 'foobar.com']
#
# # 嵌套属性
# # 当使用属性解析模型时，模型实例将从顶级属性和 deeper-nested 属性。
#
# class PetCls:
#     def __init__(self, *, name: str, species: str):
#         self.name = name
#         self.species = species
#
#
# class PersonCls:
#     def __init__(self, *, name: str, age: float = None, pets: list[PetCls]):
#         self.name = name
#         self.age = age
#         self.pets = pets
#
#
# class Pet(BaseModel):
#     model_config = ConfigDict(from_attributes=True)
#
#     name: str
#     species: str
#
#
# class Person(BaseModel):
#     model_config = ConfigDict(from_attributes=True)
#
#     name: str
#     age: float = None
#     pets: list[Pet]
#
#
# bones = PetCls(name='Bones', species='dog')
# orion = PetCls(name='Orion', species='cat')
# anna = PersonCls(name='Anna', age=20, pets=[bones, orion])
# anna_model = Person.model_validate(anna)
# print('支持嵌套属性 ：', anna_model)
# """
# name='Anna' age=20.0 pets=[Pet(name='Bones', species='dog'), Pet(name='Orion', species='cat')]
# """
#
# # 验证数据
# print('--------------------------- 验证数据 --------------------------------------')
# # model_validate（） ：这与模型的方法非常相似， 只是它需要一个字典或一个对象，而不是关键字参数。如果无法验证传递的对象， 或者，如果它不是相关模型的字典或实例，则会引发 ValidationError。__init__
# # model_validate_json（））：这将验证提供的数据是否为 JSON 字符串或对象。 如果您的传入数据是 JSON 负载，则通常认为这更快（而不是手动将数据解析为字典）。 在文档的 JSON 部分中了解有关 JSON 解析的更多信息。bytes
# # model_validate_strings（）：它接受一个带有字符串键和值的字典（可以嵌套），并以 JSON 模式验证数据，以便可以将所述字符串强制转换为正确的类型。
#
# # 校验dict 是不是符合 User 类型的；如果是，就转成 User 类型；如果不是，就抛出异常
# m = User.model_validate({'id': 123, 'name': 'James'})
# print('字典 2 user : ', type(m), ';;', m)
# # 隐式数据类型转换
# m = User.model_validate_strings({'id': '123', 'name': 'James'})
# print('字符串字典 2 user : ', type(m), ';;', m)
#
# m = User.model_validate_json('{"id": 123, "name": "James"}')
# print('json字符串  2 user : ', type(m), ';;', m)
#
# # 错误处理
# print('--------------------------- 错误处理 --------------------------------------')
#
# # 创建未经验证的模型
# print('--------------------------- 创建未经验证的模型 --------------------------------------')
# # Pydantic 还提供了 model_construct（） 方法，该方法允许在不进行验证的情况下创建模型。 这至少在少数情况下很有用：
# #
# # 当处理已知有效的复杂数据时（出于性能原因）
# # 当一个或多个验证器函数是非幂等的
# # 当一个或多个验证程序函数具有您不希望触发的副作用时。
# user = User.model_construct()
# print('创建未经验证的模型 ：', user)
#
# # 通用模型
# print('--------------------------- 通用模型: （自定义类型 / 泛型模型） --------------------------------------')
# # 定义泛型类型
# DataT = TypeVar('DataT')
#
#
# # 定义普通模型
# class DataModel(BaseModel):
#     number: int
#
#
# # 定义泛型模型
# class Response(BaseModel, Generic[DataT]):
#     data: DataT
#
#
# # 创建泛型模型对象，并指定泛型类型int
# print('泛型类型是 int : ', Response[int](data=1))
# # > data=1
# print('泛型类型是 str : ', Response[str](data='value'))
# # > data='value'
# print('泛型类型是 DataModel : ', Response[DataModel](data=DataModel(number=23)))
# print('泛型类型是 DataModel 并转成字典: ', Response[DataModel](data=DataModel(number=23)).model_dump())
# # 泛型类型是 DataModel :  data=DataModel(number=23)
# # 泛型类型是 DataModel 并转成字典:  {'data': {'number': 23}}
#
# # 泛型继承，字类继承父类的泛型类型
# TypeX = TypeVar('TypeX')
#
#
# class BaseClass(BaseModel, Generic[TypeX]):
#     X: TypeX
#
#
# class ChildClass(BaseClass[TypeX], Generic[TypeX]):
#     pass
#
#
# # Parametrize `TypeX` with `int`:
# print('泛型继承 ： ', ChildClass[int](X=1))
#
#
# class ChildClass2(BaseClass[str], Generic[TypeX]):
#     pass
#
#
# # print('泛型指定 ： ', ChildClass2(X=1)) #报错 ，因为 ChildClass2 已经指定了泛型类型，所以不能再指定泛型类型
# print('泛型指定 ： ', ChildClass2[str](X='hellow'))  # 正确
#
# # 复杂泛型
# TypeY = TypeVar('TypeY')
# TypeZ = TypeVar('TypeZ')
#
#
# class BaseClass(BaseModel, Generic[TypeX, TypeY]):
#     x: TypeX
#     y: TypeY
#
#     def say(self):
#         return "父类实例方法 >>>> "
#
#     @classmethod
#     def eat(cls):
#         return "父类类方法 >>>> "
#
#     @staticmethod
#     def run():
#         return "父类静态方法 >>>> "
#
#     def say_2(self):
#         return "父类实例方法 >>>> "
#
#     @classmethod
#     def eat_2(cls):
#         return "父类类方法 >>>> "
#
#     @staticmethod
#     def run_2():
#         return "父类静态方法 >>>> "
#
#
# class ChildClass(BaseClass[int, TypeY], Generic[TypeY, TypeZ]):
#     z: TypeZ
#
#     def say(self):
#         return "子类实例方法 >>>> "
#
#     @classmethod
#     def eat(cls):
#         return "子类类方法 >>>> "
#
#     @staticmethod
#     def run():
#         return "子类静态方法 >>>> "
#
#
# child = ChildClass[str, int](x='1', y='y', z='3')
# print('复杂泛型（多个泛型类型，其中一个已指定）:', child.model_dump())
#
# print('子类调用 say()方法 ：', child.say(), ' ; 子类调用 say_2()方法 ：', child.say_2())
# print('子类调用 eat()方法 ：', child.say(), ' ; 子类调用 eat_2()方法 ：', child.eat_2())
# print('子类调用 run()方法 ：', child.run(), ' ; 子类调用 run_2()方法 ：', child.run_2())
#
# # 验证未参数化的类型变量
#
# T = TypeVar('T')
# U = TypeVar('U', bound=int)  # 绑定为 int 类型，可认为是默认类型
# # V = TypeVar('V', default=str)  # 指定为默认类型，仅在Python 3.12+中有效
# V = TypeVar('V')
#
#
# class Model(BaseModel, Generic[T, U, V]):
#     t: T
#     u: U
#     v: V
#
#
# # u 进行了隐式类型转换
# m = Model(t=1, u='1', v='v')
# m_2 = Model(t='合肥', u=12, v='上海')  # u 必须是int类型 或是 int类型字符串
# print('Model没有指定任何类型 ：t未指定类型（可以是任意类型）:', type(m.t))
# print('Model没有指定任何类型 ：u 在创建类型时就指定了类型 int :', type(m.u))
# print('Model没有指定任何类型 ：V 指定了默认类型 :', type(m.v))
#
# print('Model没有指定任何类型 :', m_2)
#
# m_3 = Model[str, str, str](t='sdf', u=' ', v='va')
# print('Model 指定任何类型为[str, str, str]:', m_3)
#
#
# # 另一种写法
# class SubModel(Model[Any, int, V]):
#     t: Any
#     u: int
#     v: V
#
#
# sub_1 = SubModel(t=1, u='1', v='12')
# print('SubModel_1 ：', sub_1)
# sub_2 = SubModel[str](t=1, u='1', v='合肥')
# print('SubModel_2 ：', sub_2)
#
# print('--------------------------------')
# # TypeVar 可以进行全模块搜索，而class 搜索之前的类型绑定
# ItemT = TypeVar('ItemT', bound='ItemBase')
#
#
# # ... ： 表示占位符，说明类体中暂时未定义具体字段，需后续补充
# class ItemBase(BaseModel): ...
#
#
# class IntItem(ItemBase):
#     value: int
#
#
# class IntItem2(BaseModel):
#     value: int
#
#
# print('IntItem_2--> :', IntItem2(value=234))
#
#
# class ItemHolder(BaseModel, Generic[ItemT]):
#     item: ItemT
#
#
# loaded_data = {'item': {'value': 1}}
#
# print('使用默认的类型 ：', ItemHolder(**loaded_data))
# # > item=ItemBase()
#
# print('指定了 IntItem ：', ItemHolder[IntItem](**loaded_data))
# # > item=IntItem(value=1)
#
# print('指定了 IntItem2 ：', ItemHolder[IntItem2](**loaded_data))
#
#
# # > item=IntItem2(value=1)
#
#
# # 未参数化类型变量的序列化
# class ErrorDetails(BaseModel):
#     foo: str
#
#
# T = TypeVar('ErrorDataT', bound=ErrorDetails)
#
#
# class Error(BaseModel, Generic[T]):
#     message: str
#     details: T
#
#
# class MyErrorDetails(ErrorDetails):
#     bar: str
#
#
# # serialized as Any
# error = Error(
#     message='We just had an error',
#     details=MyErrorDetails(foo='var', bar='var2'),
# )
# assert error.model_dump() == {
#     'message': 'We just had an error',
#     'details': {
#         'foo': 'var',
#         'bar': 'var2',
#     },
# }
#
# # serialized using the concrete parametrization
# # note that `'bar': 'var2'` is missing
# error = Error[ErrorDetails](
#     message='We just had an error',
#     details=ErrorDetails(foo='var'),
# )
# assert error.model_dump() == {
#     'message': 'We just had an error',
#     'details': {
#         'foo': 'var',
#     },
# }
#
# # 动态模型创建
# print('---------------------- 动态模型创建 ---------------------------')
# # 动态创建模型
# DynamicFoobarModel = create_model('DynamicFoobarModel', foo=(str, '123'), bar=(int, 123))
#
# print('动态模型 ： ', DynamicFoobarModel(foo='foo', bar=12))
#
# DynamicModel = create_model(
#     'DynamicModel',
#     foo=(str, Field(alias='FOO')),
#     bar=Annotated[str, Field(description='Bar field')],
#     _sss=(int, PrivateAttr(default=1)),
# )
#
#
# # Field（） 与 Annotated[数据类型，字段元信息，校验器] 都是创建字段的语法糖
# #  Annotated[] 是替换 Field() 的语法糖
# class DynamicModel2(BaseModel):
#     foo: str = Field(alias='FOO')
#     bar: Annotated[str, Field(description='Bar field')]
#     _private: int
#
#
# # 添加验证器
# def alphanum(cls, v):
#     # isalnum() ： 判断是否是 字母、数字 组成的字符串
#     print('---------校验器执行了：', v)
#     assert v.isalnum(), 'must be alphanumeric'
#     return v
#
#
# #  字典 key 可以随便定义，field_validator('username') 函数参数必须是 要校验的类字段名称
# validators = {
#     'username2_validator': field_validator('username')(alphanum)
# }
#
# UserModel = create_model(
#     'UserModel', username=(str, ...), __validators__=validators
# )
# user = UserModel(username='scolvin')
# print(user)
# # > username='scolvin'
# try:
#     UserModel(username='scolvi%n')
# except ValidationError as e:
#     print('错误信息 ：', e)
#
# # RootModel和自定义根类型
# print('---------------------- RootModel和自定义根类型 ---------------------------')
# Pets = RootModel[list[str]]
# PetsByName = RootModel[dict[str, str]]
# print('RootModel 泛型类型是 list[str] : ', Pets(['dog', 'cat']))
# print('RootModel 泛型类型是 dict[str, str] : ', PetsByName({'Otis': 'dog', 'Milo': 'cat'}))
#
# import abc
# # 抽象基类
# print('---------------------- 抽象基类 ---------------------------')
# class FooBarModel(BaseModel, abc.ABC):
#     a: str
#     b: int
#
#     @abc.abstractmethod
#     def my_abstract_method(self):
#         pass
